package com.afdor.rws.core.extension.spring;

import com.afdor.rws.annotation.extension.Extension;
import com.afdor.rws.core.extension.ExtensionFactory;
import com.afdor.rws.core.extension.ExtensionLoader;
import com.afdor.rws.core.extension.factory.SpringExtensionFactory;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.filter.AnnotationTypeFilter;

import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Objects;
import java.util.Set;

/**
 * 基于Spring实现的扩展点扫描器
 *
 * @author 悭梵
 * @date Created in 2018-09-14 9:53
 */
public class ExtensionScanner extends ClassPathBeanDefinitionScanner {

    private Class<? extends Annotation> annotationClass = Extension.class;

    public ExtensionScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public ExtensionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
        super(registry, useDefaultFilters);
    }

    public ExtensionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment) {
        super(registry, useDefaultFilters, environment);
    }

    public ExtensionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters, Environment environment, ResourceLoader resourceLoader) {
        super(registry, useDefaultFilters, environment, resourceLoader);
    }

    public void setAnnotationClass(Class<? extends Annotation> annotationClass) {
        this.annotationClass = annotationClass;
    }

    public void registerFilters() {
        boolean acceptAllInterfaces = true;

        addIncludeFilter((metadataReader, metadataReaderFactory) -> Objects.equals(SpringExtensionFactory.class.getName(), metadataReader.getClassMetadata().getClassName()));
        // if specified, use the given annotation and / or marker interface
        if (this.annotationClass != null) {
            addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
            acceptAllInterfaces = false;
        }

        if (acceptAllInterfaces) {
            // default include filter that accepts all classes
            addIncludeFilter((metadataReader, metadataReaderFactory) -> Boolean.TRUE);
        }

        // exclude package-info.java
        addExcludeFilter((metadataReader, metadataReaderFactory) -> {
            if (!metadataReader.getAnnotationMetadata().getAnnotationTypes().contains(Extension.class.getName())) {
                return true;
            }
            String[] interfaceNames = metadataReader.getClassMetadata().getInterfaceNames();
            for (String interfaceName : interfaceNames) {
                if (StringUtils.equalsAny(interfaceName, ExtensionLoader.class.getName(), ExtensionFactory.class.getName())) {
                    return true;
                }
            }
            if (metadataReader.getClassMetadata().getClassName().endsWith("package-info")) {
                return true;
            }
            return false;
        });
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        }
        return beanDefinitions;
    }
}
